﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using WMFInterface;
using WMFWrapper;

namespace WMVStreamEditor
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow
	{
		public MainWindow()
		{
			InitializeComponent();
			sources_ = new Dictionary<string, SourceInfo>();
		}


		/// <summary>
		/// Удаляет ноду из дерева
		/// </summary>
		/// <param name="key"></param>
		private void DeleteNode(string key)
		{
			selectedNodeKey_ = null;
			sources_.Remove(key);
			for (var i = 0; i < sourcesTree.Items.Count; ++i)
			{
				var item = (TreeViewItem)sourcesTree.Items[i];
				if ((string)item.Tag != key) continue;
				sourcesTree.Items.RemoveAt(i);
				break;
			}
			UpdateButtons();
		}

		private void UpdateButtons()
		{
			UpdateRemoveButton();
			UpdateProcessButton();
		}
		
		/// <summary>
		/// Обновление кнопки Process
		/// </summary>
		private void UpdateProcessButton()
		{
			processButton.IsEnabled = false;
			var hasAudio = false;
			var hasVideo = false;
			var hasScript = false;
			var hasHeaderScript = false;
			foreach (var f in sources_)
			{
				foreach(var s in f.Value.Streams)
				{
					if (!s.Selected)
						continue;
					switch (s.Type)
					{
						case StreamType.Video:
							if (hasVideo)
							{
								statusString.Content = Resources["TooManyStreams"];
								return;
							}
							hasVideo = true;
							break;
						case StreamType.Audio:
							if (hasAudio)
							{
								statusString.Content = Resources["TooManyStreams"];
								return;
							}
							hasAudio = true;
							break;
						case StreamType.Script:
							if (hasScript)
							{
								statusString.Content = Resources["TooManyStreams"];
								return;								
							}
							hasScript = true;
							break;
						default:
							statusString.Content = Resources["OtherStreams"];
							return;
					}
				}
			    if (!f.Value.ScriptsActive)
			    {
			        continue;
			    }
			    if (hasHeaderScript)
			    {
			        statusString.Content = Resources["TooManyScripts"];
			        return;
			    }
			    hasHeaderScript = true;
			}
			if (hasVideo || hasAudio)
			{
				processButton.IsEnabled = true;
				statusString.Content = Resources["ReadyToProcess"];
				return;
			}			
			statusString.Content = hasScript || hasHeaderScript ? Resources["OnlyScript"] : Resources["NoStreams"];
			return;
		}

		/// <summary>
		/// Обновление состояния кнопки Remove
		/// </summary>
		private void UpdateRemoveButton()
		{
			removeButton.IsEnabled = selectedNodeKey_ != null;
		}

		private static T FindParent<T>(FrameworkElement current) where T : FrameworkElement
		{
			T result;
			do
			{
				current = (FrameworkElement) current.Parent;
				result = current as T;
			}
			while (result == null);
			return result;
		}

		#region Event handlers
		/// <summary>
		/// Обработчик кнопки Add
		/// </summary>
		private void AddButton_Click(object sender, RoutedEventArgs e)
		{
			var open = new OpenFileDialog { Filter = InputFilter_, Multiselect = true };
			if (!open.ShowDialog(this).GetValueOrDefault(false)) return;
			foreach (var f in open.FileNames)
			{
				try
				{
					var si = new SourceInfo(f);
					if (sources_.ContainsKey(f))
					{
						DeleteNode(f);
					}
					sources_.Add(f, si);
					var hdr = new HeaderControl
					          	{
					          		Text = si.Name
					          		,
					          		InUse = { IsChecked = false }
					          	};
					hdr.InUse.Click += CheckClicked;
					var ti = new TreeViewItem { Header = hdr, Tag = f };
					ti.Selected += TreeItemSelected;
					for (var i = 0; i < si.Streams.Length; ++i)
					{
						var ii = new ItemInfo { Key = f, StreamIndex = i };
						var stream = si.Streams[i];
						var hdr2 = new HeaderControl
						           	{
						           		Text = String.Format("{0}: {1} {2:0.0} kbps [{3}]"
						           		                     , stream.Number, stream.Type, stream.BitRate / 1024.0, stream.Language)
						           	};
						hdr2.InUse.Click += CheckClicked;
						var ti2 = new TreeViewItem { Header = hdr2, Tag = ii, Focusable = false };
						ti.Items.Add(ti2);
					}
					if (si.ScriptsCount > 0)
					{
						var script = new ScriptInfo {Key = f};
						var hdr3 = new HeaderControl
						           	{
						           		Text = String.Format("Header scripts: {0}"
						           		                     , si.ScriptsCount)
						           	};
						hdr3.InUse.Click += CheckClicked;
						var ti3 = new TreeViewItem {Header = hdr3, Tag = script, Focusable = false};
						ti.Items.Add(ti3);
					}
					sourcesTree.Items.Add(ti);
				}
				catch (Exception ex)
				{
					MessageBox.Show(this, ex.ToString(), "File open error", MessageBoxButton.OK, MessageBoxImage.Error);
				}
				UpdateButtons();
			}
		}
		/// <summary>
		/// Обаботчик кнопки Remove
		/// </summary>
		private void RemoveButton_Click(object sender, RoutedEventArgs e)
		{
			var item = (TreeViewItem)sourcesTree.SelectedItem;
			DeleteNode((string)item.Tag);
		}
		/// <summary>
		/// Обработчик события выбора элемента
		/// </summary>
		private void TreeItemSelected(object sender, RoutedEventArgs e)
		{
			var ti = (TreeViewItem)sender;
			selectedNodeKey_ = (string)ti.Tag;
			UpdateRemoveButton();
		}

		/// <summary>
		/// Обработчик события клика на чекбоксе
		/// </summary>
		private void CheckClicked(object sender, RoutedEventArgs e)
		{
			var cb = (CheckBox)sender;
			if (cb.IsChecked == null)
			{
				cb.IsChecked = false;
			}
			var check = (bool)cb.IsChecked;

			var hdr = FindParent<HeaderControl>(cb);
			var ti = (TreeViewItem)hdr.Parent;
			var key = ti.Tag as string;
			if (key != null) //файл
			{
				var item = sources_[key];
				foreach (var s in item.Streams)
				{
					s.Selected = check;
				}
				item.ScriptsActive = check;
				foreach (TreeViewItem i in ti.Items)
				{
					var hdr2 = (HeaderControl)i.Header;
					hdr2.InUse.IsChecked = check;
				}
			}
			else //стрим
			{
				SourceInfo item;
				var ii = ti.Tag as ItemInfo;
				if (ii != null)
				{
					item = sources_[ii.Key];
					item.Streams[ii.StreamIndex].Selected = check;
				}
				else //скрипты
				{
					var si = (ScriptInfo)ti.Tag;
					item = sources_[si.Key];
					item.ScriptsActive = check;
				}
				var checkedCount = item.ScriptsActive ? 1 : 0;
				foreach (var i in item.Streams)
				{
					if (i.Selected)
						++checkedCount;
				}
				ti = (TreeViewItem)ti.Parent;
				hdr = (HeaderControl)ti.Header;
				hdr.InUse.IsChecked = checkedCount != 0
					// ReSharper disable ConditionIsAlwaysTrueOrFalse
					? (checkedCount != item.Streams.Length + 1 ? null : (bool?)true) : false;
				// ReSharper restore ConditionIsAlwaysTrueOrFalse					
			}
			UpdateProcessButton();
		}

		/// <summary>
		/// Закрытие приложения по кнопке Exit
		/// </summary>
		private void ExitButton_Click(object sender, RoutedEventArgs e)
		{
			Close();
		}

		/// <summary>
		/// Обработка стримов
		/// </summary>
		private void ProcessButton_Click(object sender, RoutedEventArgs e)
		{
			var sfd = new SaveFileDialog { Filter = InputFilter_ };
		    if (sfd.ShowDialog().GetValueOrDefault(false))
			{
				var sources = new List<WMFInterface.SourceInfo>();
				var streams = new List<Stream>();
				foreach (var v in sources_)
				{
					foreach (var s in v.Value.Streams)
					{
						if (s.Selected)
							streams.Add(s.Stream);
					}
					if (streams.Count == 0 && !v.Value.ScriptsActive) continue;
					sources.Add(new WMFInterface.SourceInfo(v.Key, v.Value.ScriptsActive, streams.ToArray()));
					streams.Clear();
				}
				try
				{
					statusString.Content = Resources["Processing"];
					AsfCopier.CopyStreams(sources.ToArray(), sfd.FileName);
					statusString.Content = Resources["Done"];
				}
				catch (Exception ex)
				{
					MessageBox.Show(this, ex.ToString(), "File save error", MessageBoxButton.OK, MessageBoxImage.Error);
				}
			}
		}
		#endregion

		/// <summary>
		/// Источники
		/// </summary>
		private readonly Dictionary<string, SourceInfo> sources_;
		/// <summary>
		/// Ключ выбранной ноды
		/// </summary>
		private string selectedNodeKey_;

		private const string InputFilter_ = "Windows Media Files|*.wmv;*.wma;*.asf|All files|*.*";
	}

	internal class ItemInfo
	{
		public string Key;
		public int StreamIndex;
	}

	internal class ScriptInfo
	{
		public string Key;
	}
}
